package tsl

import (
	"errors"
)


type Property interface {
	GetId() string
	GetName() string
	GetType() string
	GetUnit() string
	GetExpands() map[string]string
	IsObject() (*PropertyObject, bool)
	setId(string)
	setType(string)
}

type BaseProperty struct {
	Id          string            `json:"id"`
	Name        string            `json:"name"`
	Type        string            `json:"type"`
	Unit        string            `json:"unit,omitempty"`
	Expands     map[string]string `json:"expands,omitempty"`
	Description string            `json:"description,omitempty"`
}

func (p *BaseProperty) GetId() string {
	return p.Id
}
func (p *BaseProperty) GetName() string {
	return p.Name
}

func (p *BaseProperty) GetUnit() string {
	return p.Unit
}

func (p BaseProperty) GetExpands() map[string]string {
	return p.Expands
}

func (p *BaseProperty) IsObject() (*PropertyObject, bool) {
	return nil, false
}

func (p *BaseProperty) setId(t string) {
	p.Id = t
}
func (p *BaseProperty) setType(t string) {
	p.Type = t
}

type Elements struct {
	Text  string `json:"text"`
	Value string `json:"value"`
}

type PropertyEnum struct {
	BaseProperty
	Elements []Elements `json:"elements"`
}

func NewPropertyEnum(id, name string, elements []Elements) *PropertyEnum {
	p := &PropertyEnum{BaseProperty: BaseProperty{Id: id, Name: name}, Elements: elements}
	p.setType(p.GetType())
	return p
}
func (p *PropertyEnum) GetType() string {
	return TypeEnum
}

func (v *PropertyEnum) Valid() error {
	if len(v.Elements) == 0 {
		return errors.New("enum elements is empty")
	}
	for _, v := range v.Elements {
		if len(v.Value) == 0 {
			return errors.New("enum elements value is empty")
		}
		err := idCheck(v.Value)
		if err != nil {
			return err
		}
	}
	return nil
}

type PropertyInt struct {
	BaseProperty
	Unit string `json:"unit"`
}

func NewPropertyInt(id, name string) *PropertyInt {
	p := &PropertyInt{BaseProperty: BaseProperty{Id: id, Name: name}}
	p.setType(p.GetType())
	return p
}

func (p *PropertyInt) GetType() string {
	return TypeInt
}

type PropertyLong struct {
	PropertyInt
}

func NewPropertyLong(id, name string) *PropertyLong {
	p := &PropertyLong{PropertyInt: *NewPropertyInt(id, name)}
	p.setType(p.GetType())
	return p
}

func (p *PropertyLong) GetType() string {
	return TypeLong
}

type PropertyBool struct {
	BaseProperty
	TrueText   string `json:"trueText"`
	TrueValue  string `json:"trueValue"`
	FalseText  string `json:"falseText"`
	FalseValue string `json:"falseValue"`
}

func NewPropertyBool(id, name string) *PropertyBool {
	p := &PropertyBool{BaseProperty: BaseProperty{Id: id, Name: name}}
	p.setType(p.GetType())
	return p
}
func (p *PropertyBool) GetType() string {
	return TypeBool
}

type PropertyString struct {
	BaseProperty
	Max int32 `json:"max"`
}

func NewPropertyString(id, name string) *PropertyString {
	p := &PropertyString{BaseProperty: BaseProperty{Id: id, Name: name}}
	p.setType(p.GetType())
	return p
}

func (p *PropertyString) GetType() string {
	return TypeString
}

type PropertyDate struct {
	BaseProperty
	Format string `json:"format"`
}

func NewPropertyDate(id, name string) *PropertyDate {
	p := &PropertyDate{BaseProperty: BaseProperty{Id: id, Name: name}}
	p.setType(p.GetType())
	return p
}

func (p *PropertyDate) GetType() string {
	return TypeDate
}

type PropertyPassword struct {
	PropertyString
}

func NewPropertyPassword(id, name string) *PropertyPassword {
	p := &PropertyPassword{PropertyString: *NewPropertyString(id, name)}
	p.setType(p.GetType())
	return p
}

func (p *PropertyPassword) GetType() string {
	return TypePassword
}

type PropertyFile struct {
	BaseProperty
	BodyType string `json:"bodyType"` // url, base64
}

func NewPropertyFile(id, name string) *PropertyFile {
	p := &PropertyFile{BaseProperty: BaseProperty{Id: id, Name: name}}
	p.setType(p.GetType())
	return p
}
func (p *PropertyFile) GetType() string {
	return TypeFile
}

type PropertyFloat struct {
	BaseProperty
	Scale int32  `json:"scale"`
	Unit  string `json:"unit"`
}

func NewPropertyFloat(id, name string) *PropertyFloat {
	p := &PropertyFloat{BaseProperty: BaseProperty{Id: id, Name: name}}
	p.setType(p.GetType())
	return p
}
func (p *PropertyFloat) GetType() string {
	return TypeFloat
}

type PropertyDouble struct {
	PropertyFloat
}

func NewPropertyDouble(id, name string) *PropertyDouble {
	p := &PropertyDouble{PropertyFloat: *NewPropertyFloat(id, name)}
	p.setType(p.GetType())
	return p
}
func (p *PropertyDouble) GetType() string {
	return TypeDouble
}

type PropertyObject struct {
	BaseProperty
	Properties []Property `json:"properties"`
}

func NewPropertyObject(id, name string, properties []Property) *PropertyObject {
	p := &PropertyObject{BaseProperty: BaseProperty{Id: id, Name: name}, Properties: properties}
	p.setType(p.GetType())
	return p
}
func (p *PropertyObject) GetType() string {
	return TypeObject
}

func (p *PropertyObject) IsObject() (*PropertyObject, bool) {
	return p, true
}

func (p *PropertyObject) PropertiesMap() map[string]Property {
	tslP := map[string]Property{}
	for _, p := range p.Properties {
		tslP[p.GetId()] = p
	}
	return tslP
}